#include "XenRender.h"

using namespace std;

bool XenRender::fastClick = false;
bool XenRender::created = false;
bool XenRender::detached = false;
//MyGUI::Gui* XenRender::mGUI = false;
//MyGUI::DirectXPlatform* XenRender::mPlatform = NULL;

XenRender::XenRender(IDirect3DDevice9 *pIDirect3DDevice9) 
{
	this->pIDirect3DDevice9 = pIDirect3DDevice9;
	/*gCamera = new CFPCamera(D3DXVECTOR3(0,0,-12.0f));
	moveCam = false;*/
	Initialize();
}

void XenRender::Initialize()
{
	ConfigFile::Load();

	xenMemory = new XenMemory();

	xenStats = new XenStats(pIDirect3DDevice9);
	xenInfo = new XenInfo(pIDirect3DDevice9);
#ifndef LITE
	xenMap = new XenMap(pIDirect3DDevice9);
	xenItemSniffer = new XenItemSniffer(pIDirect3DDevice9);
#endif

	D3DDISPLAYMODE displayMode;
	pIDirect3DDevice9->GetDisplayMode(0, &displayMode);

	D3DVIEWPORT9 viewport;
	pIDirect3DDevice9->GetViewport(&viewport);

	screenWidth = viewport.Width;
	screenHeight = viewport.Height;

	ZeroMemory(&fVersion, sizeof(D3DXFONT_DESC));
	strcpy(fVersion.FaceName, "Arial");
	fVersion.Height = -12;
	D3DXCreateFontIndirect(pIDirect3DDevice9, &fVersion, &versionFont);

	if(!created) {
		HANDLE fastClickThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)this->ProcessFastClick, NULL, 0, NULL);
		SetThreadPriority(fastClickThread, THREAD_PRIORITY_LOWEST);

#ifndef LITE
		xenItemSniffer->StartSnifferThread();
		PacketHandler::Attach();
		//Utils::Log(LOGFILE, "Sniffer Attached\n");
#endif

		
		/*mPlatform = new MyGUI::DirectXPlatform();
		mPlatform->initialise(pIDirect3DDevice9);

		RECT rect = { 0, 0, 0, 0 };
		GetClientRect(xenHwnd, &rect);
		int width = rect.right - rect.left;
		int height = rect.bottom - rect.top;

		//resizeRender(width, height);

		if (mPlatform)
			mPlatform->getRenderManagerPtr()->setViewSize(width, height);

		setupResources();

		mGUI = new MyGUI::Gui();
		mGUI->initialise();

		MyGUI::ButtonPtr button = mGUI->createWidget<MyGUI::Button>("Button", 100, 100, 300, 26, MyGUI::Align::Default, "Main");
		button->setCaption("exit");*/
		// set callback
		//button->eventMouseButtonClick = MyGUI::newDelegate(CLASS_POINTER, &CLASS_NAME::METHOD_NAME); // CLASS_POINTER is pointer to instance of a CLASS_NAME (usually this)
		// or
		//button->eventMouseButtonClick = MyGUI::newDelegate(STATIC_METHOD_NAME);
		//button->eventMouseButtonClick = MyGUI::newDelegate(GLOBAL_FUNC_NAME);

		created = true;
	}
}

void XenRender::setupResources()
{
	/*MyGUI::xml::Document doc;

	if (!doc.open(string("resources.xml")))
		doc.getLastError();

	MyGUI::xml::ElementPtr root = doc.getRoot();
	if (root == nullptr || root->getName() != "Paths")
		return;

	MyGUI::xml::ElementEnumerator node = root->getElementEnumerator();
	while (node.next())
	{
		if (node->getName() == "Path")
		{
			bool root = false;
			if (node->findAttribute("root") != "")
			{
				root = MyGUI::utility::parseBool(node->findAttribute("root"));
				//if (root) mRootMedia = node->getContent();
			}
			mPlatform->getDataManagerPtr()->addResourceLocation(node->getContent(), false);
		}
	}*/
}



void XenRender::Render()
{
	if (xenMemory->ReadPage() == 1) { //Main menu
		//Show version of Xenergy
		RECT loadedTextRect = { 10, 10, 0, 0 };
		versionFont->DrawText(NULL, XENERGYLOAD, -1, &loadedTextRect, DT_CALCRECT, 0);
		versionFont->DrawText(NULL, XENERGYLOAD, -1, &loadedTextRect, DT_CENTER, D3DCOLOR_XRGB(255, 255, 255));
	} else if (xenMemory->ReadPage() == 0) {

		if(fastClick) {
			RECT fastClickTextRect = { screenWidth - 200, screenHeight - 50, 0, 0 };
			versionFont->DrawText(NULL, FASTCLICKSTR, -1, &fastClickTextRect, DT_CALCRECT, 0);
			versionFont->DrawText(NULL, FASTCLICKSTR, -1, &fastClickTextRect, DT_RIGHT, D3DCOLOR_XRGB(255, 255, 255));
		}
		
		
/*
		// Prints out long location text on top of character
		char stringText[255];
		int xLocLong = ReadMemLocUInt(113495864);
		int yLocLong = ReadMemLocUInt(113495868);
		int screenCenterX = screenWidth/2;
		int screenCenterY = screenHeight/2;
		
		RECT LongLocTextRect = {screenCenterX-100, screenCenterY - 50, screenCenterX+100, screenCenterY+50};
		sprintf_s(stringText, 255, "%d %d", xLocLong, yLocLong);

		versionFont->DrawText(NULL, stringText, -1, &LongLocTextRect, DT_CALCRECT, 0);
		versionFont->DrawText(NULL, stringText, -1, &LongLocTextRect, DT_CENTER, D3DCOLOR_XRGB(255, 255, 255));
*/
	}

	//mPlatform->getRenderManagerPtr()->drawOneFrame();

	xenStats->Render();
	xenInfo->Render();
#ifndef LITE
	xenMap->Render();
	xenItemSniffer->Render();
#endif

	xenStats->ProcessInput();
	xenInfo->ProcessInput();
#ifndef LITE
	xenMap->ProcessInput();
#endif

	ProcessInput();
}


long WINAPI XenRender::ProcessFastClick(void *param)
{
	fastClick = false;
	static bool keyReleased = true;

	while (true) {
		if (Input::KeysDown(ConfigFile::FastClickInput)) {
			if(keyReleased) {
				if (fastClick) {
					fastClick = false;
					Utils::Log(LOGFILE, "Fast Click Disabled\n");
					//SetThreadPriority(fastClickThread, THREAD_PRIORITY_LOWEST);
				} else { 
					fastClick = true;	
					Utils::Log(LOGFILE, "Fast Click Enabled\n");
					//SetThreadPriority(fastClickThread, THREAD_PRIORITY_NORMAL);
				}
			}
			keyReleased = false;
		} else {
			keyReleased = true;
		}

		if (fastClick) {
			if (KEYDOWN(VK_RBUTTON)) {
				mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
				Sleep(10);
				mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);				
				Sleep(10);
			} else {
				Sleep(250);
			}
		} else { 
			Sleep(35);
		}
	}

	return 0;
}

void XenRender::ProcessInput() 
{
	static bool keyReleased = true;

	if (Input::KeysDown(VK_PRINT) || Input::KeysDown(VK_SNAPSHOT))
	{
		if(keyReleased) {
			if(GetFileAttributes(SCREENSHOTS_FOLDER) == INVALID_FILE_ATTRIBUTES)
				_mkdir(SCREENSHOTS_FOLDER);

			time_t rawtime;
			time (&rawtime);
			string strTime = ctime(&rawtime);
			strTime.erase(0, 4); // Remove Day of the Week
			strTime.erase(15, 6); //Remove the year and \n
			strTime.replace(9, 1, "-");
			strTime.replace(12, 1, "-");
			TakeScreenShotDC(string(string(SCREENSHOTS_FOLDER) + "\\" + strTime + ".bmp").c_str());
		}
		keyReleased = false;
	} else {
		keyReleased = true;
	}

	/*if (Input::KeysDown(VK_RIGHT))
	{
		Utils::Log(LOGFILE, "Trying to move cam\n");
		D3DXMATRIX viewMatrix;
		//pIDirect3DDevice9->GetTransform( D3DTS_VIEW, &viewMatrix );

		pIDirect3DDevice9->BeginScene();
		CFPCamera cam(D3DXVECTOR3(0,100,-12.0f));
		cam.MoveRight(100);
		cam.CalculateViewMatrix(&viewMatrix);
		pIDirect3DDevice9->SetTransform( D3DTS_VIEW, &viewMatrix );
		pIDirect3DDevice9->EndScene();
		pIDirect3DDevice9->Present( NULL, NULL, NULL, NULL ); 

		//}

		const float kCameraMovementSpeed=5;
		const float kCameraRotationSpeed=0.05;
		if (Input::KeysDown(VK_UP)) {
			gCamera->MoveForward(kCameraMovementSpeed);
			//moveCam = true;
		}
		if (Input::KeysDown(VK_DOWN)) {
			gCamera->MoveForward(-kCameraMovementSpeed);
			//moveCam = true;
		}
		if (Input::KeysDown(VK_RIGHT)) {
			gCamera->MoveRight(kCameraMovementSpeed);
			//moveCam = true;
		}
		if (Input::KeysDown(VK_LEFT)) {
			gCamera->MoveRight(-kCameraMovementSpeed);
			//moveCam = true;
		}
		if (Input::KeysDown(VK_HOME)) {
			gCamera->MoveUp(kCameraMovementSpeed);
			//moveCam = true;
		}
		if (Input::KeysDown(VK_END)) {
			gCamera->MoveUp(-kCameraMovementSpeed);
			//moveCam = true;
		}
		if (Input::KeysDown(VK_NUMPAD4)) {
			gCamera->Yaw(-kCameraRotationSpeed);
			//moveCam = true;
		}
		if (Input::KeysDown(VK_NUMPAD6)) {
			gCamera->Yaw(kCameraRotationSpeed);
			//moveCam = true;
		}
		if (Input::KeysDown(VK_NUMPAD8)) {
			gCamera->Pitch(-kCameraRotationSpeed);
			//moveCam = true;
		}
		if (Input::KeysDown(VK_NUMPAD2)) {
			gCamera->Pitch(kCameraRotationSpeed);
			//moveCam = true;
		} 
		if (Input::KeysDown(VK_NUMPAD7)) {
			gCamera->Roll(-kCameraRotationSpeed);
			//moveCam = true;
		}
		if (Input::KeysDown(VK_NUMPAD9)) {
			gCamera->Roll(kCameraRotationSpeed);
			//moveCam = true;
		}
		if (Input::KeysDown(VK_F9)) {
			moveCam = true;
		}
		if (Input::KeysDown(VK_F8)) {
			moveCam = false;
		}
	}*/
}


void XenRender::TakeScreenShotDC(const char* fileName)
{
	// Create a normal DC and a memory DC for the entire screen. The 
	// normal DC provides a "snapshot" of the screen contents. The 
	// memory DC keeps a copy of this "snapshot" in the associated 
	// bitmap. 
	//HDC hdcScreen = CreateDC("DISPLAY", NULL, NULL, NULL);
	D3DVIEWPORT9 viewport;
	pIDirect3DDevice9->GetViewport(&viewport);

	RECT rc;
	GetWindowRect(GetForegroundWindow(), &rc);

	HDC hdcScreen = GetDC(NULL);
	HDC hdcCompatible = CreateCompatibleDC(hdcScreen); 

	// Create a compatible bitmap for hdcScreen. 

	HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, rc.right - rc.left, rc.bottom - rc.top); 

	if (hBitmap == 0) 
		Utils::Log(LOGFILE, "hbmScreen Error\r\n");

	// Select the bitmaps into the compatible DC. 
	if (!SelectObject(hdcCompatible, hBitmap)) 
		Utils::Log(LOGFILE, "Compatible Bitmap Selection\r\n");

	//Copy color data for the entire display into a 
	//bitmap that is selected into a compatible DC. 
	if (!BitBlt(hdcCompatible, 
		0,0, 
		rc.right - rc.left, rc.bottom - rc.top,
		hdcScreen, 
		rc.left, rc.top,
		SRCCOPY)) 
		Utils::Log(LOGFILE, "Screen to Compat Blt Failed\r\n");

	SaveBitmap(fileName, hBitmap);
	ReleaseDC(NULL, hdcScreen);
	DeleteDC(hdcScreen);
	ReleaseDC(NULL, hdcCompatible);
	DeleteDC(hdcCompatible);
	DeleteObject(hBitmap);
}

void XenRender::SaveBitmap(const char *szFilename, HBITMAP hBitmap)
{
	HDC hdc = NULL;
	FILE* fp = NULL;
	LPVOID pBuf = NULL;
	BITMAPINFO bmpInfo;
	BITMAPFILEHEADER bmpFileHeader; 

	do { 
		hdc = GetDC(NULL);
		ZeroMemory(&bmpInfo, sizeof(BITMAPINFO));
		bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		GetDIBits(hdc, hBitmap, 0 ,0, NULL, &bmpInfo, DIB_RGB_COLORS); 

		if(bmpInfo.bmiHeader.biSizeImage <= 0)
			bmpInfo.bmiHeader.biSizeImage = bmpInfo.bmiHeader.biWidth * abs(bmpInfo.bmiHeader.biHeight) * (bmpInfo.bmiHeader.biBitCount + 7) / 8;

		if((pBuf = malloc(bmpInfo.bmiHeader.biSizeImage)) == NULL) {
			Utils::Log(LOGFILE, "Unable to Allocate Bitmap Memory\r\n");
			break;
		}           

		bmpInfo.bmiHeader.biCompression = BI_RGB;
		GetDIBits(hdc, hBitmap, 0, bmpInfo.bmiHeader.biHeight, pBuf, &bmpInfo, DIB_RGB_COLORS);       
		if((fp = fopen(szFilename,"wb")) == NULL) {
			Utils::Log(LOGFILE, "Unable to Create Bitmap File ");
			Utils::Log(LOGFILE, szFilename);
			Utils::Log(LOGFILE, "\r\n");
			break;
		} 

		bmpFileHeader.bfReserved1 = 0;
		bmpFileHeader.bfReserved2 = 0;
		bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + bmpInfo.bmiHeader.biSizeImage;
		bmpFileHeader.bfType = 'MB';
		bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); 

		fwrite(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, fp);
		fwrite(&bmpInfo.bmiHeader, sizeof(BITMAPINFOHEADER), 1, fp);
		fwrite(pBuf, bmpInfo.bmiHeader.biSizeImage, 1, fp); 
	} while(false); 

	if(hdc) {
		ReleaseDC(NULL,hdc); 
		//free(hdc); 
	}
	if(pBuf)    
		free(pBuf); 
	if(fp)      
		fclose(fp);
}



void XenRender::TestCam()
{
	/*if (readMemData.ReadInGameVar() == 2) {
		if (moveCam)
			gCamera->CalculateViewMatrix(&viewMatrix);
		pIDirect3DDevice9->SetTransform( D3DTS_VIEW, &viewMatrix );
		//moveCam = false;
	}*/
}


void XenRender::Release()
{	
	
	if(!detached) {
		if (PacketHandler::attached)
			//Utils::Log(LOGFILE, "Attached\n");

		
		//PacketHandler::Detach(); //FAILS!!!
		detached = true;
		//Utils::Log(LOGFILE, "Sniffer Detached\n");
	}

	versionFont->Release();

	xenStats->Release();
	xenInfo->Release();
#ifndef LITE
	xenMap->Release();
	xenItemSniffer->Release();
#endif

	delete(xenStats);
	delete(xenInfo);
	delete(xenMemory);
#ifndef LITE
	delete(xenMap);
	delete(xenItemSniffer);
#endif
}

int ReadMemLocUInt(int address)
{
	HANDLE Process = GetCurrentProcess();
	unsigned int memLocInt;
	ReadProcessMemory(Process, (LPCVOID)address, &memLocInt, sizeof(int), NULL);

	return memLocInt;
}